use honeybee::mongodb::{
    bson::{self, doc},
    options::{FindOneOptions, UpdateOptions},
};
use honeybee::{prelude::Service, Result};
use serde::{Deserialize, Serialize};

use crate::uid_producer;

use super::collections;

#[derive(Serialize, Deserialize, Debug)]
pub struct Account {
    pub account: String,
    pub uid: i64,
    pub timestamp: i64,
}

async fn get(account: &str) -> Result<Option<Account>> {
    let account_coll = collections::account();

    let option = FindOneOptions::builder()
        .projection(Some(doc! {"_id": false}))
        .build();

    account_coll
        .find_one(doc! {"account": account}, option)
        .await
        .map(|doc| doc.and_then(|doc| bson::from_document(doc).ok()))
        .map_err(|err| err.into())
}

pub async fn get_or_create(account: &str) -> Result<Account> {
    let account_coll = collections::account();

    let uid_producer = uid_producer::UidProducer::from_registry().await?;
    let uid = uid_producer.call(uid_producer::Acquire).await?;

    let filter = doc! {"account": account};
    let account_doc = Account {
        account: account.into(),
        uid,
        timestamp: chrono::Local::now().timestamp(),
    };
    let update = doc! {"$setOnInsert": bson::to_document(&account_doc)?};
    let options = UpdateOptions::builder().upsert(true).build();
    let _ = account_coll.update_one(filter, update, options).await?;
    if let Some(account) = get(account).await? {
        return Ok(account);
    } else {
        return Err(anyhow!("get account:{} failed", account));
    }
}
